/**
* file name  : Value.java
* authors    : Luciano Xumerle
* created    : lun 06 mar 2006 11:27:15 CET
*
* Copyright (c) 2004-2005 Luciano Xumerle. All rights reserved.
*
* This file is part of gephinfo.
*
* gephinfo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
*/

package it.ciano.jenoware;

import java.lang.Float;

/**
 * The class stores the value (String or numeric) of a Phenotype or Allele.
 *
 * @author Luciano Xumerle
 * @version 0.0.1
 */
public class Value implements Comparable
{
    // GLOBAL PRIVATE VARS
    private String _str;
    private float _num;
    private boolean _numeric;
    private boolean _valid;
    boolean _zeroISNULL;

    /**
     * Class Constructor.
     *
     * @param val the value.
     * @param zeroISNULL if true then zero will be a null value.
     */
    public Value ( String val, boolean zeroISNULL )
    {
        _zeroISNULL = zeroISNULL;
        _numeric = false;
        _valid = false;
        if ( val == null )
            _str = "";
        else
            _str = val.trim();
        if ( _str.equals ( "" )
                || ( _str.equals ( "0" ) && _zeroISNULL )
                || _str.toLowerCase().equals ( "x" ) )
        {
            _str = "";
            _num = 0;
        }
        else
        {
            _valid = true;
            Float pp = getFloat ( _str );
            if ( pp != null )
            {
                _num = pp.floatValue();
                _numeric = true;
            }
        }
    }

    /**
     * Returns the value.
     *
     * @return the value.
     */
    public String getValue()
    {
        return _str;
    }

    /**
     * Returns the numeric value.
     *
     * @return the numeric value (0 if allele isn't numeric).
     */
    public float getNumValue()
    {
        return _num;
    }

    /**
     * Checks if the value is valid.
     * The set of not valid values are: "", "0", and "x".
     *
     * @return true or false.
     */
    public boolean isValid()
    {
        return _valid;
    }

    /**
     * Checks if the value is numeric.
     *
     * @return true or false.
     */
    public boolean isNumeric()
    {
        return _numeric;
    }

    /**
     * Returns the (rounded) integer value.
     * If Value is float then returns the rounded value: Math.round( _num );
     *
     * @return the integer value.
     */
    public int getInt()
    {
        return Math.round ( _num );
    }

    /**
     * Returns true if value is even (Ex: 112, 114, 116, ...).
     * The integer value is retrieved by the getInt() method.
     *
     * @return true or false.
     */
    public boolean isEven()
    {
        return ( ( getInt() % 2 ) == 0 );
    }

    /**
     * Returns true if value is odd (Ex: 111, 113, 115, ...).
     * The integer value is retrieved by the getInt() method.
     *
     * @return true or false.
     */
    public boolean isOdd()
    {
        return ( !isEven() );
    }

    /**
     * Returns the exact distance between two numeric Value.
     * The distance is: Math.abs( a.getNumValue() - getNumValue() );
     *
     * @param a the numeric Value.
     * @return the distance (-1 if Value objects aren't numeric).
     */
    final public float getDistance ( Value a )
    {
        if ( a != null && a.isNumeric() && isNumeric() )
            return Math.abs ( a.getNumValue() - getNumValue() );
        return -1;
    }

    /**
      * The method converts String to Float if possible.
      *
      * @param num the value.
      * @return the Float object (null if a NumberFormatException occours).
      */
    final public static Float getFloat ( String num )
    {
        try
        {
            return new Float ( num );
        }
        catch ( NumberFormatException e )
        {
            return null;
        }
    }

    /**
     * Overriding toString method.
     *
     * @return the value.
     */
    public String toString()
    {
        return _str;
    }

    /**
     * Overriding equals method.
     *
     * @param o the object to compare.
     * @return true or false.
     */
    public boolean equals ( Object o )
    {
        Value vv = ( Value ) o;
        return ( getValue().equals ( vv.getValue() ) );
    }

    /**
     * Overriding clone method.
     *
     * @return a new cloned object ID
     */
    public Object clone()
    {
        Value cloned = new Value ( getValue(), _zeroISNULL );
        return ( Object ) cloned;
    }

    /**
     * Overriding compareTo method.
     *
     * @param o the object to compare.
     * @return an integer less or equal or major of 0.
     */
    public int compareTo ( Object o )
    {
        final int BEFORE = -1;
        final int EQUAL = 0;
        final int AFTER = 1;
        final Value oth = ( Value ) o;

        if ( getValue().equals ( oth.getValue() ) )
            return EQUAL;

        if ( isNumeric() && oth.isNumeric() )
        {
            if ( _num < oth.getNumValue() )
                return BEFORE;
            else
                return AFTER;
        }
        return getValue().compareTo ( oth.getValue() );
    }

    /**
     * Prints to STDOUT the distances between Adjacent Elements in Value objects List.
     *
     * @param title a String.
     * @param aa the Value list (Values must be valid and numeric).
     */
    final public static void printValuesDistances ( String title, Value[] aa )
    {
        System.out.println ( title );
        if ( aa != null )
            for ( int y = 0;y < aa.length; y++ )
            {
                float f = 0;
                if ( y > 1 )
                    f = aa[ y ].getDistance ( aa[ y - 1 ] );
                System.out.println ( "Allele: " + aa[ y ] + " distance with previous: " + f );
            }
    }


} // end class

